﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.IO;

namespace GW2LastOnline
{
    public partial class formMain : Form
    {
        private enum FilterRegion { EU = 0, NA = 1 };
        private enum FilterBy { World = 0, Guild = 1 };

        private DataGridViewColumn sortedColumn;
        private bool loginAsked;

        private Session session;
        private Achievements achievements;
        private Achievements.AchievementLists[] achievementLists;

        private formFind find;
        private string[] filterFind;
        private int visibleRows;

        public formMain()
        {
            InitializeComponent();

            gridRecords.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.None;
            gridRecords.AdvancedColumnHeadersBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.Single;

            session = new Session();
            achievements = new Achievements(session);
            achievementLists = new Achievements.AchievementLists[2];

            achievements.OnAchievementsPageUpdated += new Achievements.OnAchievementsPageUpdatedEventHandler(achievements_OnAchievementsPageUpdated);
            achievements.OnListsUpdated += new Achievements.OnListsUpdatedEventHandler(achievements_OnListsUpdated);
        }

        void achievements_OnListsUpdated(Achievements.AchievementLists lists)
        {
            try
            {
                this.BeginInvoke(new MethodInvoker(delegate
                    {
                        progressLoading.Visible = false;
                        achievementLists[comboRegion.SelectedIndex] = lists;

                        if (comboFilterBy.SelectedIndex == (int)FilterBy.World)
                            SelectList(lists.worlds);
                        else if (comboFilterBy.SelectedIndex == (int)FilterBy.Guild)
                            SelectList(lists.guilds);
                    }));
            }
            catch { }
        }

        void achievements_OnAchievementsPageUpdated(Achievements.AchievementsPage page)
        {
            try
            {
                this.BeginInvoke(new MethodInvoker(delegate
                {
                    foreach (Achievements.PlayerRecord record in page.records)
                        AddRow(record);
                    if (gridRecords.SortedColumn != null && page.records.Count > 0)
                        gridRecords.Sort(gridRecords.SortedColumn, (gridRecords.SortOrder == SortOrder.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending);
                    progressLoading.Visible = page.hasNext;

                    if (page.records.Count == 0 && !session.IsValid() && comboFilterBy.SelectedIndex == (int)FilterBy.Guild)
                        SelectFilterBy(FilterBy.Guild);
                }));
            }
            catch { }
        }

        private bool Login()
        {
            loginAsked = true;

            formLogin login = new formLogin();
            if (login.ShowDialog(this) == DialogResult.OK)
            {
                session.Cookies = login.Cookies;
                return true;
            }

            return false;
        }

        private void AddRow(string name, int points, DateTime updated)
        {
            DataGridViewRow row = gridRecords.Rows[gridRecords.Rows.Add()];
            row.Cells["recordName"].Value = name;
            row.Cells["recordPoints"].Value = points;
            row.Cells["recordUpdated"].Value = new LastUpdatedCellDisplay(updated);
            row.Cells["recordUpdated"].ToolTipText = updated.ToLongDateString() + " " + updated.ToLongTimeString();
            row.Selected = false;

            if (ApplyFilter(row, filterFind))
                visibleRows++;

            UpdateDisplaying();
        }

        private void UpdateDisplaying()
        {
            if (this.InvokeRequired)
            {
                try
                {
                    this.BeginInvoke(new MethodInvoker(UpdateDisplaying));
                }
                catch { }
                return;
            }

            if (filterFind != null && visibleRows != gridRecords.Rows.Count)
                labelTotal.Text = "Displaying " + visibleRows + " of " + gridRecords.Rows.Count + " record" + ((gridRecords.Rows.Count == 1) ? "" : "s");
            else
                labelTotal.Text = "Displaying " + gridRecords.Rows.Count + " record" + ((gridRecords.Rows.Count == 1) ? "" : "s");
        }

        private void AddRow(Achievements.PlayerRecord record)
        {
            AddRow(record.name, record.points, record.updated);
        }

        private void formMain_Load(object sender, EventArgs e)
        {

        }

        private void gridRecords_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {

        }

        private void gridRecords_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            DataGridViewColumn newColumn = gridRecords.Columns[e.ColumnIndex];
            DataGridViewColumn oldColumn = gridRecords.SortedColumn;

            if (e.Button == MouseButtons.Left && oldColumn != newColumn)
            {
                if (oldColumn != null)
                    oldColumn.HeaderCell.Style.BackColor = Color.Black;

                newColumn.HeaderCell.Style.BackColor = Color.FromArgb(47, 47, 47);
                newColumn.HeaderCell.Style.ApplyStyle(newColumn.HeaderCell.Style);
            }
        }

        private void gridRecords_Sorted(object sender, EventArgs e)
        {
            if (sortedColumn != gridRecords.SortedColumn)
            {
                if (sortedColumn != null)
                    sortedColumn.HeaderCell.Style.BackColor = Color.Black;
                gridRecords.SortedColumn.HeaderCell.Style.BackColor = Color.FromArgb(47, 47, 47);
                sortedColumn = gridRecords.SortedColumn;
            }
        }

        private class LastUpdatedCellDisplay
        {
            public LastUpdatedCellDisplay(DateTime date)
            {
                this.Date = date;
            }

            public DateTime Date
            {
                get;
                set;
            }

            public override string ToString()
            {
                TimeSpan t = DateTime.Now.Subtract(this.Date);
                int days = (int)t.TotalDays;

                if (days < 1)
                    return "Today";
                else if (days < 2)
                    return "Yesterday";
                else if (days < 30)
                    return (int)t.TotalDays + " days ago";
                else if (days < 365)
                {
                    int months = days / 30;
                    if (months == 1)
                        return "Last month";
                    else
                        return months + " months ago";
                }
                else
                {
                    int years = days / 365;
                    if (years == 1)
                        return "Last year";
                    else
                        return years + " years ago";
                }
            }
        }

        private void gridRecords_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
        {
            if (e.Column.Name == "recordUpdated")
            {
                e.Handled = true;
                e.SortResult = ((LastUpdatedCellDisplay)e.CellValue1).Date.CompareTo(((LastUpdatedCellDisplay)e.CellValue2).Date);
            }
        }

        private void comboFilterBy_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboFilter.Visible = (comboFilterBy.SelectedIndex >= 0))
            {
                comboFilter.Items.Clear();
                SelectFilterBy((FilterBy)comboFilterBy.SelectedIndex);
            }
        }

        private void comboRegion_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboRegion.SelectedIndex != -1)
                SelectRegion((FilterRegion)comboRegion.SelectedIndex);
        }

        private void SelectRegion(FilterRegion region)
        {
            comboFilter.Items.Clear();

            if (achievementLists[(int)region].worlds == null)
            {
                if (!session.IsValid() && !loginAsked)
                    Login();

                progressLoading.Visible = true;
                achievements.RequestLists((region == FilterRegion.EU) ? Achievements.REGION_EU : Achievements.REGION_NA);
            }
            else
                SelectFilterBy((FilterBy)comboFilterBy.SelectedIndex);
        }

        private void SelectList(List<string> list)
        {
            string selected = (string)comboFilter.SelectedItem;
            comboFilter.Items.Clear();

            if (list == null)
                return;

            int index = -1;
            foreach (string item in list)
            {
                comboFilter.Items.Add(item);
                if (item == selected)
                    index = comboFilter.Items.Count - 1;
            }

            if (index != -1)
                comboFilter.SelectedIndex = index;
        }

        private void SelectFilterBy(FilterBy filter)
        {
            if (comboRegion.SelectedIndex == -1)
                return;

            if (filter == FilterBy.Guild)
            {
                if (!session.IsValid())
                {
                    if (Login())
                    {
                        progressLoading.Visible = true;
                        achievements.RequestLists((comboRegion.SelectedIndex == (int)FilterRegion.EU) ? Achievements.REGION_EU : Achievements.REGION_NA);
                        return;
                    }
                }
            }

            if (filter == FilterBy.Guild)
                SelectList(achievementLists[comboRegion.SelectedIndex].guilds);
            else
                SelectList(achievementLists[comboRegion.SelectedIndex].worlds);
        }

        private void comboFilter_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboFilter.SelectedIndex != -1 && comboRegion.SelectedIndex != -1)
            {
                gridRecords.Rows.Clear();
                visibleRows = 0;
                UpdateDisplaying();

                if (comboFilterBy.SelectedIndex == (int)FilterBy.Guild)
                {
                    progressLoading.Visible = true;
                    achievements.RequestGuild((comboRegion.SelectedIndex == (int)FilterRegion.EU) ? Achievements.REGION_EU : Achievements.REGION_NA, (string)comboFilter.SelectedItem);
                }
                else if (comboFilterBy.SelectedIndex == (int)FilterBy.World)
                {
                    progressLoading.Visible = true;
                    achievements.RequestWorld((comboRegion.SelectedIndex == (int)FilterRegion.EU) ? Achievements.REGION_EU : Achievements.REGION_NA, (string)comboFilter.SelectedItem);
                }
            }
        }

        private void findToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (find == null)
            {
                find = new formFind();
                find.OnFilterChanged += new formFind.OnFilterChangedEventHandler(find_OnFilterChanged);
                find.Show(this);
            }
            else
            {
                if (find.Visible)
                    find.Focus();
                else
                    find.Show(this);
            }
        }

        private bool ApplyFilter(DataGridViewRow row, string[] filters)
        {
            string value = (string)row.Cells["recordName"].Value;
            bool matches = false;

            if (filters == null)
                matches = true;
            else
                foreach (string filter in filters)
                {
                    if (value.IndexOf(filter, 0, StringComparison.InvariantCultureIgnoreCase) >= 0)
                    {
                        matches = true;
                        break;
                    }
                }

            if (this.InvokeRequired)
                this.BeginInvoke(new MethodInvoker(delegate
                    {
                        row.Visible = matches;
                    }));
            else
                row.Visible = matches;

            return matches;
        }

        private void ApplyFilter()
        {
            string[] filter = filterFind;
            int count = gridRecords.Rows.Count;

            new Thread(new ThreadStart(delegate
                {
                    int matching = 0;
                    for (int i = 0; i < count && filter == filterFind; i++)
                    {
                        if (ApplyFilter(gridRecords.Rows[i], filter))
                            matching++;
                    }
                    if (filter == filterFind)
                    {
                        visibleRows = matching;
                        UpdateDisplaying();
                    }
                })).Start();
        }

        void find_OnFilterChanged(string filter)
        {
            if (filter.Length > 0)
                filterFind = filter.Split(' ');
            else
                filterFind = null;

            try
            {
                this.BeginInvoke(new MethodInvoker(delegate
                    {
                        ApplyFilter();
                    }));
            }
            catch { }
        }

        private void exportlistToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Text Document|*.txt|CSV|*.csv";
            dialog.Title = "Export list";

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                StreamWriter stream  = null;
                try
                {
                    stream = new StreamWriter(File.OpenWrite(dialog.FileName));

                    if (dialog.FilterIndex == 2)
                    {
                        stream.WriteLine("name,last updated,achievement points");
                    }

                    for (int i = 0; i < gridRecords.Rows.Count; i++)
                    {
                        DataGridViewRow row = gridRecords.Rows[i];

                        DateTime d = ((LastUpdatedCellDisplay)row.Cells["recordUpdated"].Value).Date;
                        if (dialog.FilterIndex == 1)
                        {
                            stream.WriteLine((string)row.Cells["recordName"].Value);
                            stream.WriteLine("Achievements: " + (int)row.Cells["recordPoints"].Value);
                            stream.WriteLine("Last updated: " + d.ToShortDateString() + " " + d.ToLongTimeString());
                            stream.WriteLine();
                        }
                        else if (dialog.FilterIndex == 2)
                        {
                            stream.WriteLine((string)row.Cells["recordName"].Value + "," + d.ToShortDateString() + " " + d.ToLongTimeString() + "," + (int)row.Cells["recordPoints"].Value);
                        }
                    }

                    stream.Flush();
                    stream.Close();
                    stream = null;
                }
                catch (Exception err)
                {
                    MessageBox.Show("An error occured while trying to write the file:\n\n" + err.Message, "An error has occured", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    if (stream != null)
                        stream.Close();
                }
            }
        }

        private void formMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            achievements.Abort();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}
